#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "tsocket.h"
#include "tbuffer.h"
namespace tlib
{
struct STBlock
{
  char buffer[BLOCK_SIZE];
};
TBufferPool::TBufferPool()
{
  m_pAllBlock = new STBlock[BLOCK_COUNT];
  for (int i = 0; i < BLOCK_COUNT; i++)
    m_nNoUseBlock.push_back(i);
}

TBufferPool::~TBufferPool()
{
  delete[] m_pAllBlock;
}

int TBufferPool::AllocBlock()
{
  //  TAutoLock lock(&m_mutex);
  int blockNo = m_nNoUseBlock.back();
  m_nNoUseBlock.pop_back();
  return blockNo;
}

void TBufferPool::FreeBlock(int blockNo)
{
  //  TAutoLock lock(&m_mutex);
  m_nNoUseBlock.push_back(blockNo);
}

STBlock*
TBufferPool::GetBlock(int blockNo)
{
  STBlock* st = &m_pAllBlock[blockNo];
  return st;
}

TBuffer::TBuffer() :
  m_nReadNo(-1), m_nWriteNo(-1), m_nReadIdx(0), m_nWriteIdx(0)
{

}

TBuffer::~TBuffer()
{
  for (unsigned int i = 0; i < m_blocks.size(); i++)
  {
    m_pBufferPool->FreeBlock(m_blocks[i]);
  }
}

void TBuffer::Bind(TBufferPool* pool)
{
  m_pBufferPool = pool;
}

bool TBuffer::Read(char* buff, int len)
{
  if (len <= 0 || GetLength() == 0)
    return false;

  int newReadNo = m_nReadNo, newReadIdx = m_nReadIdx;
  for (; len > 0;)
  {
    STBlock* st = m_pBufferPool->GetBlock(m_blocks[newReadNo]);
    int ableReadLen = m_nWriteNo > newReadNo ? BLOCK_SIZE - newReadIdx : m_nWriteNo == newReadNo ? m_nWriteIdx - newReadIdx : 0;
    ableReadLen = ableReadLen > len ? len : ableReadLen;
    if (ableReadLen > 0)
    {
      memcpy(buff, &st->buffer[newReadIdx], ableReadLen);
      buff += ableReadLen;
      len -= ableReadLen;
      newReadIdx += ableReadLen;
      if (newReadIdx == BLOCK_SIZE)
      {
        newReadNo++;
        newReadIdx = 0;
      }
    }
  }
  return true;
}

bool TBuffer::Write(char* buff, int len)
{
  if (m_blocks.empty())
  {
    m_blocks.push_back(m_pBufferPool->AllocBlock());
    m_nReadNo = 0;
    m_nWriteNo = 0;
  }
  for (; len > 0;)
  {
    STBlock* st = m_pBufferPool->GetBlock(m_blocks[m_nWriteNo]);
    int ableWriteLen = BLOCK_SIZE - m_nWriteIdx;
    ableWriteLen = ableWriteLen > len ? len : ableWriteLen;
    if (ableWriteLen > 0)
    {
      memcpy(&st->buffer[m_nWriteIdx], buff, ableWriteLen);
      buff += ableWriteLen;
      len -= ableWriteLen;
      m_nWriteIdx += ableWriteLen;
      if (m_nWriteIdx == BLOCK_SIZE && m_nWriteNo == m_blocks.size() - 1)
      {
        m_blocks.push_back(m_pBufferPool->AllocBlock());
        m_nWriteNo++;
        m_nWriteIdx = 0;
      }
    }
  }
  return true;
}

bool TBuffer::Remove(int len)
{
  if (len <= 0 || GetLength() == 0)
    return false;

  for (; len > 0;)
  {
    //    STBlock* st = m_pBufferPool->GetBlock(m_blocks[m_nReadNo]);
    int ableReadLen = m_nWriteNo > m_nReadNo ? BLOCK_SIZE - m_nReadIdx : m_nWriteNo == m_nReadNo ? m_nWriteIdx - m_nReadIdx : 0;
    ableReadLen = ableReadLen > len ? len : ableReadLen;
    if (ableReadLen > 0)
    {
      len -= ableReadLen;
      m_nReadIdx += ableReadLen;
      if (m_nReadIdx == BLOCK_SIZE)
      {
        m_nReadNo++;
        m_nReadIdx = 0;
      }
    }
  }
  for (; m_nReadNo > 0 || (0 == m_nReadNo && 0 == m_nWriteNo && m_nReadIdx == m_nWriteIdx);)
  {
    m_nReadNo--;
    m_nWriteNo--;
    m_pBufferPool->FreeBlock(*m_blocks.begin());
    m_blocks.erase(m_blocks.begin());
  }
  return true;
}

int TBuffer::GetLength()
{
  if (m_blocks.empty())
    return 0;
  int ableReadLen = (m_nWriteNo * BLOCK_SIZE + m_nWriteIdx) - (m_nReadNo * BLOCK_SIZE + m_nReadIdx);
  if (ableReadLen <= 0)
    return 0;
  return ableReadLen;
}

int TBuffer::GetSepLength(char* sep, int len)
{
  int allAbleReadLen = GetLength();
  if (0 == allAbleReadLen)
    return 0;

  int newReadNo = m_nReadNo, newReadIdx = m_nReadIdx, allLen = 0;
  for (; allAbleReadLen > 0;)
  {
    STBlock* st = m_pBufferPool->GetBlock(m_blocks[newReadNo]);
    int ableReadLen = m_nWriteNo > newReadNo ? BLOCK_SIZE - newReadIdx : m_nWriteNo == newReadNo ? m_nWriteIdx - newReadIdx : 0;
    ableReadLen = ableReadLen > allAbleReadLen ? allAbleReadLen : ableReadLen;
    if (ableReadLen > 0)
    {
      char* chr = &st->buffer[newReadIdx];
      for (int i = 0; i <= ableReadLen; i++)
      {
        if (chr[i] == *sep)
        {
          if (!memcmp(&chr[i], sep, len))
            return allLen;
        }
        allLen++;
      }
      allAbleReadLen -= ableReadLen;
      newReadIdx += ableReadLen;
      if (newReadIdx == BLOCK_SIZE)
      {
        newReadNo++;
        newReadIdx = 0;
      }
    }
  }
  return 0;
}
} //namespace tlib
